package com.oyjd.framework.ui.gallery;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Scroller;

/**
 * 3D相册视图类
 * @author dengweiming
 */
public class Gallery extends FrameLayout {
	
	// ==================控制滑动的成员=================================
	static final int FINISHED		= 0;
	static final int ON_FLING		= 1;
	static final int ON_SCROLL		= 2;
	Scroller mScroller;
	int mState;

	int mMinScroll;
	int mMaxScroll;
	int mLastScreenPos;		// 最后一屏的位置
	int mScroll;
	
	int mScreenCount = 1;		// 为了防止除0，初始化为1
	int mScreenWidth = 1;		// 为了防止除0，初始化为1
	int mScreenHeight;
	int mCurrentScreen;
	int mDstScreen;
	
	int mTouchDownScreen;
	int mTouchDownP;
	int mTouchDownX;
	int mTouchDownY;
	int mLastTouchP;
	int mLastTouchX;
	int mLastTouchY;
	int mTouchSlop;
	boolean mScrollStarted;
	
	static final int FLING_VELOCITY = 400;
	VelocityTracker mVelocityTracker;
	int mFlingVelocity;
	int mScrollingDuration = 600;		// 切屏最大时间限制
	int mDecelerateDuration = 200;
	Interpolator mInterpolator = new OvershootInterpolator(0);
	
	
	// ==================控制绘制的成员=================================
	final static int Count = 4;
	final static float Angle = 10;
	final static float SinAngle = (float)Math.sin(Math.toRadians(Angle));
	final static float CenterXRatio = 0.2f;
	final static float CenterYRatio = 0.25f;
	
	final static PaintFlagsDrawFilter DrawFilter = new PaintFlagsDrawFilter(0, 
			Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
	
	Camera mCamera = new Camera();
	Matrix[] mMatrix = new Matrix[Count + 1];
	float[] mClipX = new float[Count + 1];
	float[] mPoints = new float[2];
	
	// ==================                =================================
	GalleryController mController;
	Paint mPaint = new Paint();
	boolean mOptimizeClipRectEnabled = false;
	
	public Gallery(Context context) {
		this(context, null);
	}
	
	public Gallery(Context context, AttributeSet att) {
		super(context, att);
		setVerticalScrollBarEnabled(false);
		setHorizontalScrollBarEnabled(false);
		final ViewConfiguration configuration = ViewConfiguration.get(context);
		mTouchSlop = configuration.getScaledTouchSlop();
		for(int i = 0; i <= Count; ++i){
			mMatrix[i] = new Matrix(); 
		}
	}

	/**
	 * 设置控制器，以动态加载图片和响应点击事件及切屏事件
	 * @param controller
	 */
	void setController(GalleryController controller) {
		mController = controller;
		if(controller != null){
			controller.mGallery = this;
		}
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		abortAnimation();
		mScreenWidth = w;
		mScreenHeight = h;
		// 取消当前方向的偏移量
		mScroll = 0;
		scrollBy(-getScrollX(), 0);
		final int oldCount = mScreenCount;
		mScreenCount = -1;	// 为了使updateScreenGroupChildCount中重新计算
		setScreenCount(oldCount);
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		setScreenCount(getChildCount());
		if(mController != null){
			mController.onScreenChanged(mCurrentScreen, mCurrentScreen);
		}
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		mLastTouchX = (int)event.getX();
		mLastTouchY = (int)event.getY();
		int delta = mLastTouchP - mLastTouchX;
		mLastTouchP = mLastTouchX;
		if(mVelocityTracker == null){
			mVelocityTracker = VelocityTracker.obtain();
		}
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
			{
				mVelocityTracker.addMovement(event);
				mTouchDownP = mLastTouchP;
				mTouchDownX = mLastTouchX;
				mTouchDownY = mLastTouchY;
				mTouchDownScreen = mCurrentScreen;
				if(mState != FINISHED){
					mState = ON_SCROLL;
				}
			}
				break;
			case MotionEvent.ACTION_MOVE:
			{
				if(null==this.mController)return true;
				this.mController.isautomove=false;
				if(!mScrollStarted && isFinished()){
					if(Math.abs(mLastTouchX - mTouchDownX) > mTouchSlop 
					   || Math.abs(mLastTouchY - mTouchDownY) > mTouchSlop){
						mScrollStarted = true;
						event.setAction(MotionEvent.ACTION_DOWN);
						onTouchEvent(event);
					}
				}
				else{
					mVelocityTracker.addMovement(event);
					final int newScroll = mScroll + delta;
					if(newScroll < 0 || newScroll >= mLastScreenPos){
						delta /= 2;
						// 限制 mMinScroll <= mScroll + delta <= mMaxScroll
						delta = Math.max(mMinScroll - mScroll, Math.min(delta, mMaxScroll - mScroll));
					}		
					if(delta != 0){
						if(mState == FINISHED){
							mState = ON_SCROLL;
						}
						scrollScreenGroup(mScroll + delta);
					}
				}
			}
				break;
			case MotionEvent.ACTION_UP:
			{
				if(!mScrollStarted && isFinished()){
					if(mController != null){
						mController.onClick(mCurrentScreen);
					}
				}
				else{
					mScrollStarted = false;
					mVelocityTracker.addMovement(event);
					mVelocityTracker.computeCurrentVelocity(1000);
					mFlingVelocity = (int)mVelocityTracker.getXVelocity();
					if(mFlingVelocity > FLING_VELOCITY && mTouchDownP < mLastTouchP){
						flingToScreen(mTouchDownScreen - 1, mScrollingDuration);
					}
					else if(mFlingVelocity < -FLING_VELOCITY && mTouchDownP > mLastTouchP){
						flingToScreen(mTouchDownScreen + 1, mScrollingDuration);
					}
					else{
						mFlingVelocity = FLING_VELOCITY;
						flingToScreen(computeScreenIndex(mScroll), mScrollingDuration);
					}
				}
				mVelocityTracker.recycle();
				mVelocityTracker = null;
			}
				break;
		}
		return true;
	}

	@Override
	public void computeScroll() {
    	if(mState == ON_FLING && mScroller != null){
    		if(mScroller.computeScrollOffset()){
    			 scrollScreenGroup(mScroller.getCurrX());
    		}
    		else{
    			mState = FINISHED;
    			if(mController != null){
    				mController.onScrollFinish(mDstScreen);
    			}
    		}
    		postInvalidate();
    	}    		
	}
	
	/**
	 * 设置图片数量
	 * @param count
	 */
	void setScreenCount(int count){
		abortAnimation();
		if(mScreenCount == count){
			return;
		}
		if(count <= 0){
			return;
		}
		mScreenCount = count;
		mLastScreenPos = mScreenWidth * (mScreenCount - 1);
		
		// 限制最小值和最大值，防止computeScreenIndex()的结果越界
		mMinScroll = Math.max(-(int)(mScreenWidth * 0.5f), 
							  -mScreenWidth / 2);
		mMaxScroll = Math.min(mLastScreenPos + (int)(mScreenWidth * 0.5f), 
							  mLastScreenPos + mScreenWidth / 2 - 1);
		mMaxScroll = Math.max(mMinScroll, mMaxScroll);
		
		// 重设当前滚动量
		scrollScreenGroup(mDstScreen * mScreenWidth);
	}
	
	/**
	 * 滚动屏幕
	 * @param newScroll		新的滚动位置
	 */
	void scrollScreenGroup(int newScroll) {
        if(mScroll != newScroll){
        	final int oldScreen = mCurrentScreen;
        	final int delta = newScroll - mScroll;
        	mScroll = newScroll;
    		scrollBy(delta, 0);
	        mCurrentScreen = computeScreenIndex(mScroll);
	        if(oldScreen != mCurrentScreen){
	        	if(mController != null){
	        		mController.onScreenChanged(mCurrentScreen, oldScreen);
	        	}
	        }
        }
	}
	
	/**
	 * 获取当前显示的索引
	 * @return
	 */
	public int getCurrentIndex() {
		return mCurrentScreen;
	}

	/**
	 * 是否完成滑动动画
	 * @return
	 */
    public boolean isFinished() {
    	return mState == FINISHED;
    }

    /**
     * 强制完成滑动动画
     */
	void abortAnimation() {
		if(mState == ON_FLING){
			mState = FINISHED;
			if(mScroller != null){
				mScroller.abortAnimation();
			}
		}
	}
	
	/**
	 * 根据滚动偏移量计算处于哪一索引
	 * @param scroll		滚动偏移量
	 * @return
	 */
	int computeScreenIndex(int scroll) {
		return (scroll + mScreenWidth / 2) / mScreenWidth;
	}
	
	/**
	 * 甩动到指定屏
	 * @param dstScreen		目标索引
	 * @param duration		最长时间限制，毫秒为单位
	 */
	void flingToScreen(int dstScreen, int duration){
		Interpolator interpolator = mInterpolator;
		// 在两端采用默认插值器；在中间采用指定的插值器
		if(mScroll < 0 || dstScreen < 0){
			dstScreen = 0;
			duration = mDecelerateDuration;
			interpolator = null;
		}
		else if(mScroll > mLastScreenPos || dstScreen >= mScreenCount){			
			dstScreen = mScreenCount - 1;
			duration = mDecelerateDuration;
			interpolator = null;
		}
		
		mDstScreen = Math.max(0, Math.min(dstScreen, mScreenCount - 1));
		final int delta = mDstScreen * mScreenWidth - mScroll;
		if(delta == 0){
			mState = FINISHED;
			if(mController != null){
				mController.onScrollFinish(mDstScreen);
			}
			return;
		}
		if(mFlingVelocity != 0 && interpolator != null){
			// 计算一个合理的时间，但是限制最大值，不能太慢
			duration = Math.min(duration, Math.abs(delta * 3000 / mFlingVelocity));
			mFlingVelocity = 0;
		}
		mState = ON_FLING;
		mScroller = new Scroller(getContext(), interpolator);
		mScroller.startScroll(mScroll, 0, delta, 0, duration);
		postInvalidate();
	}

    @Override
    protected void dispatchDraw(Canvas canvas) {
		DrawFilter filter = canvas.getDrawFilter();
		canvas.setDrawFilter(DrawFilter);
		canvas.translate(mScroll, 0);
		float fIndex = mScroll / (float)mScreenWidth;
		int index = mScroll / mScreenWidth;
		int lastIndex = index + Count - 1;
		if(index * mScreenWidth == mScroll){
			--lastIndex;
		}
		for(int i = lastIndex; i >= index - 1; --i){
			final Matrix matrix = mMatrix[i - index + 1];
			mCamera.save();
			final float di = i - fIndex;
			mCamera.translate(mScreenWidth * 0.05f, mScreenHeight * -0.006f, mScreenWidth * SinAngle * 2.3f);
			if(di < 0){
				mCamera.translate(mScreenWidth * di, 0, mScreenWidth * -di*0.5f);
				mCamera.rotateY(Angle * 2 * -di);
			}
			mCamera.rotateY(-Angle);
			mCamera.translate(mScreenWidth * 0.15f * di, 0, mScreenWidth * 0.2f * di);
			mCamera.getMatrix(matrix);
			mCamera.restore();
			matrix.preTranslate(mScreenWidth * -CenterXRatio, mScreenHeight * -CenterYRatio);
			matrix.postTranslate(mScreenWidth * CenterXRatio, mScreenHeight * CenterYRatio);
			mPoints[0] = getScreenInstrictWidth(i); // TODO: 小优化：在onLayout中计算好并储存
			mPoints[1] = mScreenHeight;
			matrix.mapPoints(mPoints);
			mClipX[i - index + 1] = mPoints[0];
		}
		for(int i = lastIndex; i >= index - 1; --i){
			final float di = i - fIndex;
			canvas.save();
			if(mOptimizeClipRectEnabled){
				int clipLeft = di < 1 ? 0 : (int)mClipX[i - index];
				/**
				 * 注意！这里做了优化。
				 * 基于前面的图片高度不比后面的图片高度小，
				 * 可以将后面的图片的左边被遮挡部分裁剪掉。
				 */
				canvas.clipRect(clipLeft, 0, mScreenWidth, mScreenHeight);
			}
			canvas.concat(mMatrix[i - index + 1]);
			int alpha = 255 - (int)(di * (255 / (Count - 1.0f)));
			alpha = Math.max(0, Math.min(alpha, 255));
			drawScreen(canvas, i, alpha);
			canvas.restore();
		}
		canvas.setDrawFilter(filter);
    }
	
    /**
     * 绘制指定屏
     * @param canvas	画布
     * @param screen	指定屏的索引
     * @param alpha		不透明度
     */
    void drawScreen(Canvas canvas, int screen, int alpha) {
		ImageView view = (ImageView)getChildAt(screen);
		if(view != null){
			// 默认的绘制方法在绘制默认图片时有透明度的问题
//			view.setAlpha(alpha);
//			view.draw(canvas);
			Drawable drawable = view.getDrawable();
			if(drawable == null){
				return;
			}
            Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
            if(bitmap != null && !bitmap.isRecycled()){
            	int saveCount = canvas.save();
            	mPaint.setAlpha(alpha);
            	int width1 = ((BitmapDrawable)drawable).getIntrinsicWidth();
            	int width2 = bitmap.getWidth();
            	float scale = width1 / (float)width2;
            	canvas.concat(view.getImageMatrix());
            	canvas.scale(scale, scale);
            	Matrix m = new Matrix();
            	m.set(view.getImageMatrix());
            	Rect r = new Rect();
            	int border = 3;//边框宽度
            	int left = view.getLeft()-3;
            	int top = view.getTop()-border;
            	int right = left+bitmap.getWidth()+border*2;
            	int bottom = top+bitmap.getHeight()+border*2;
            	r.set(left, top, right, bottom);
            	Paint p = new Paint();
            	p.setColor(Color.WHITE);
            	p.setStyle(Paint.Style.FILL);
            	canvas.drawRect(r, p);//画白色边框
            	canvas.drawBitmap(bitmap, 0, 0, mPaint);            	
            	canvas.restoreToCount(saveCount);
            }
		}
    }
    
    /**
     * 设置是否优化裁剪区域功能，以提高绘制效率，前提是所有图片一样大小
     * @param enabled
     */
    void setOptimizeClipRect(boolean enabled) {
    	mOptimizeClipRectEnabled = enabled;
    }
    
    /**
     * 获取指定屏不透明部分的实际宽度
     * @param screen
     * @return
     */
    float getScreenInstrictWidth(int screen) {
    	ImageView view = (ImageView)getChildAt(screen);
		if(view != null){
			Drawable drawable = view.getDrawable();
			if(drawable != null){
				float width = drawable.getIntrinsicWidth();
				Matrix matrix = view.getImageMatrix();
				if(matrix != null){
					mPoints[0] = width;
					mPoints[1] = mScreenHeight;
					matrix.mapPoints(mPoints);
					width = mPoints[0];
				}
				return width;
			}
		}
		return 0;
    }

}
